@@ -45,7 +45,7 @@ namespace SQLite.Net2
4545 public class SQLiteConnection : IDisposable
4646 {
4747 private static readonly IDbHandle ? NullHandle = null ;
48- readonly SqliteApi sqlite = SqliteApi . Instance ;
48+ private readonly SqliteApi sqlite = SqliteApi . Instance ;
4949
5050 /// <summary>
5151 /// Used to list some code that we want the MonoTouch linker
@@ -56,17 +56,23 @@ public class SQLiteConnection : IDisposable
5656#pragma warning restore 649
5757 private readonly Random _rand = new ( ) ;
5858 private readonly ConcurrentDictionary < string , TableMapping > _tableMappings ;
59+ private readonly ConcurrentDictionary < ( string MappedTypeFullName , string Extra ) , PreparedSqlLiteInsertCommand > _insertCommandMap = new ( ) ;
60+
61+ private IColumnInformationProvider ? _columnInformationProvider ;
5962 private TimeSpan _busyTimeout ;
6063 private long _elapsedMilliseconds ;
6164 private bool _open ;
6265 private Stopwatch ? _sw ;
66+ private readonly SQLiteOpenFlags databaseOpenFlags ;
6367
64- public IBlobSerializer Serializer { get ; }
68+ public IBlobSerializer ? Serializer { get ; }
6569 public string DatabasePath { get ; }
6670 public bool StoreDateTimeAsTicks { get ; }
6771 public IDictionary < Type , string > ExtraTypeMappings { get ; }
6872 public IContractResolver Resolver { get ; }
69-
73+ public IDbHandle Handle { get ; private set ; }
74+ public bool TimeExecution { get ; set ; }
75+ public ITraceListener ? TraceListener { get ; set ; }
7076
7177 static SQLiteConnection ( )
7278 {
@@ -108,11 +114,20 @@ static SQLiteConnection()
108114 /// A contract resovler for resolving interfaces to concreate types during object creation
109115 /// </param>
110116
111- public SQLiteConnection ( string databasePath , bool storeDateTimeAsTicks = true , IBlobSerializer serializer = null , IDictionary < string , TableMapping > tableMappings = null , IDictionary < Type , string > extraTypeMappings = null , IContractResolver resolver = null )
117+ public SQLiteConnection ( string databasePath , bool storeDateTimeAsTicks = true , IBlobSerializer ? serializer = null , IDictionary < string , TableMapping > ? tableMappings = null , IDictionary < Type , string > ? extraTypeMappings = null , IContractResolver ? resolver = null )
112118 : this ( databasePath , SQLiteOpenFlags . ReadWrite | SQLiteOpenFlags . Create , storeDateTimeAsTicks , serializer , tableMappings , extraTypeMappings , resolver )
113119 {
114120 }
115121
122+ /// <summary>
123+ /// Create a new connection to the same database.
124+ /// </summary>
125+ /// <remarks>
126+ /// This support scenarios where a code needs to create a transaction, while leaving the current connection transactionless (ie: sharable with other codes), as a transaction creates a state in this object.
127+ /// </remarks>
128+ public SQLiteConnection Clone ( )
129+ => new ( DatabasePath , databaseOpenFlags , StoreDateTimeAsTicks , Serializer , _tableMappings , ExtraTypeMappings , Resolver ) ;
130+
116131 /// <summary>
117132 /// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
118133 /// </summary>
@@ -143,60 +158,40 @@ public SQLiteConnection(string databasePath, bool storeDateTimeAsTicks = true,
143158 /// <param name="resolver">
144159 /// A contract resovler for resolving interfaces to concreate types during object creation
145160 /// </param>
146-
147161 public SQLiteConnection ( string databasePath , SQLiteOpenFlags openFlags , bool storeDateTimeAsTicks = true , IBlobSerializer ? serializer = null , IDictionary < string , TableMapping > ? tableMappings = null ,
148162 IDictionary < Type , string > ? extraTypeMappings = null , IContractResolver ? resolver = null )
149163 {
150- ExtraTypeMappings = extraTypeMappings ?? new Dictionary < Type , string > ( ) ;
151- Serializer = serializer ;
152- Resolver = resolver ?? ContractResolver . Current ;
153-
154- _tableMappings = new ( tableMappings ?? new Dictionary < string , TableMapping > ( ) ) ;
155-
156164 if ( string . IsNullOrEmpty ( databasePath ) )
157- {
158- throw new ArgumentException ( "Must be specified" , "databasePath" ) ;
159- }
160-
165+ throw new ArgumentException ( "Must be specified" , nameof ( databasePath ) ) ;
161166 DatabasePath = databasePath ;
162167
163- IDbHandle handle ;
164- var r = sqlite . Open ( DatabasePath , out handle , ( int ) openFlags , null ) ;
165-
166- Handle = handle ;
168+ var r = sqlite . Open ( DatabasePath , out var handle , ( int ) openFlags , null ) ;
167169 if ( r != Result . OK )
168- {
169- throw new SQLiteException ( r , string . Format ( "Could not open database file: {0} ({1})" , DatabasePath , r ) ) ;
170- }
171-
172- if ( handle == null )
173- {
174- throw new NullReferenceException ( "Database handle is null" ) ;
175- }
176-
177- Handle = handle ;
170+ throw new SQLiteException ( r , $ "Could not open database file: { DatabasePath } ({ r } )") ;
178171
172+ Handle = handle ?? throw new NullReferenceException ( "Database handle is null" ) ;
179173 _open = true ;
180-
181- StoreDateTimeAsTicks = storeDateTimeAsTicks ;
174+ databaseOpenFlags = openFlags ;
182175
183176 BusyTimeout = TimeSpan . FromSeconds ( 0.1 ) ;
177+ Serializer = serializer ;
178+ StoreDateTimeAsTicks = storeDateTimeAsTicks ;
179+ ExtraTypeMappings = extraTypeMappings ?? new Dictionary < Type , string > ( ) ;
180+ Resolver = resolver ?? ContractResolver . Current ;
181+ _tableMappings = new ( tableMappings ?? new Dictionary < string , TableMapping > ( ) ) ;
184182 }
185183
186- private IColumnInformationProvider _columnInformationProvider ;
187184 public IColumnInformationProvider ColumnInformationProvider
188185 {
189- get { return _columnInformationProvider ; }
190- set
186+ get => _columnInformationProvider ;
187+ set
191188 {
192189 _columnInformationProvider = value ;
193190 Orm . ColumnInformationProvider = _columnInformationProvider ?? new DefaultColumnInformationProvider ( ) ;
194191 }
195192 }
196193
197- public IDbHandle Handle { get ; private set ; }
198- public bool TimeExecution { get ; set ; }
199- public ITraceListener TraceListener { get ; set ; }
194+
200195
201196 /// <summary>
202197 /// Sets a busy handler to sleep the specified amount of time when a table is locked.
@@ -440,7 +435,8 @@ public int CreateIndex(string tableName, string columnName, bool unique = false)
440435 /// <param name="columnNames">An array of column names to index</param>
441436 /// <param name="unique">Whether the index should be unique</param>
442437
443- public int CreateIndex ( string tableName , string [ ] columnNames , bool unique = false ) => CreateIndex ( tableName + "_" + string . Join ( "_" , columnNames ) , tableName , columnNames , unique ) ;
438+ public int CreateIndex ( string tableName , string [ ] columnNames , bool unique = false )
439+ => CreateIndex ( tableName + "_" + string . Join ( "_" , columnNames ) , tableName , columnNames , unique ) ;
444440
445441 /// <summary>
446442 /// Creates an index for the specified object property.
@@ -953,7 +949,7 @@ public string SaveTransactionPoint()
953949
954950 try
955951 {
956- Execute ( "savepoint " + retVal ) ;
952+ Execute ( $ "savepoint { retVal } " ) ;
957953 }
958954 catch ( Exception )
959955 {
@@ -1079,15 +1075,17 @@ private void DoSavePointExecute(string savePoint, string cmd)
10791075 /// </remarks>
10801076 public void RunInTransaction ( Action action )
10811077 {
1082- var savePoint = SaveTransactionPoint ( ) ;
1078+ string ? savePoint = null ;
10831079 try
10841080 {
1081+ savePoint = SaveTransactionPoint ( ) ;
10851082 action ( ) ;
10861083 Release ( savePoint ) ;
10871084 }
1088- catch ( Exception )
1085+ catch ( Exception e )
10891086 {
1090- RollbackTo ( savePoint , true ) ;
1087+ if ( savePoint != null )
1088+ RollbackTo ( savePoint , true ) ;
10911089 throw ;
10921090 }
10931091 }
@@ -1110,10 +1108,11 @@ public int InsertAll(IEnumerable objects, bool runInTransaction = true)
11101108 var c = 0 ;
11111109 if ( runInTransaction )
11121110 {
1113- RunInTransaction ( ( ) =>
1111+ using var db = Clone ( ) ;
1112+ db . RunInTransaction ( ( ) =>
11141113 {
11151114 foreach ( var r in objects )
1116- c += Insert ( r ) ;
1115+ c += db . Insert ( r ) ;
11171116 } ) ;
11181117 }
11191118 else
@@ -1145,10 +1144,11 @@ public int InsertAll(IEnumerable objects, string extra, bool runInTransaction =
11451144 var c = 0 ;
11461145 if ( runInTransaction )
11471146 {
1148- RunInTransaction ( ( ) =>
1147+ using var db = Clone ( ) ;
1148+ db . RunInTransaction ( ( ) =>
11491149 {
11501150 foreach ( var r in objects )
1151- c += Insert ( r , extra ) ;
1151+ c += db . Insert ( r , extra ) ;
11521152 } ) ;
11531153 }
11541154 else
@@ -1180,10 +1180,11 @@ public int InsertAll(IEnumerable objects, Type objType, bool runInTransaction =
11801180 var c = 0 ;
11811181 if ( runInTransaction )
11821182 {
1183- RunInTransaction ( ( ) =>
1183+ using var db = Clone ( ) ;
1184+ db . RunInTransaction ( ( ) =>
11841185 {
11851186 foreach ( var r in objects )
1186- c += Insert ( r , objType ) ;
1187+ c += db . Insert ( r , objType ) ;
11871188 } ) ;
11881189 }
11891190 else
@@ -1251,10 +1252,11 @@ public int InsertOrReplace(object? obj)
12511252 public int InsertOrReplaceAll ( IEnumerable objects )
12521253 {
12531254 var c = 0 ;
1254- RunInTransaction ( ( ) =>
1255+ using var db = Clone ( ) ;
1256+ db . RunInTransaction ( ( ) =>
12551257 {
12561258 foreach ( var r in objects )
1257- c += InsertOrReplace ( r ) ;
1259+ c += db . InsertOrReplace ( r ) ;
12581260 } ) ;
12591261 return c ;
12601262 }
@@ -1316,10 +1318,11 @@ public int InsertOrReplace(object obj, Type objType)
13161318 public int InsertOrReplaceAll ( IEnumerable objects , Type objType )
13171319 {
13181320 var c = 0 ;
1319- RunInTransaction ( ( ) =>
1321+ using var db = Clone ( ) ;
1322+ db . RunInTransaction ( ( ) =>
13201323 {
13211324 foreach ( var r in objects )
1322- c += InsertOrReplace ( r , objType ) ;
1325+ c += db . InsertOrReplace ( r , objType ) ;
13231326 } ) ;
13241327 return c ;
13251328 }
@@ -1382,20 +1385,20 @@ public int Insert(object? obj, string extra, Type? objType)
13821385 var insertCmd = GetInsertCommand ( map , extra ) ;
13831386 int count ;
13841387
1385- lock ( insertCmd )
1388+ try
13861389 {
13871390 // We lock here to protect the prepared statement returned via GetInsertCommand.
13881391 // A SQLite prepared statement can be bound for only one operation at a time.
1389- try
1392+ lock ( insertCmd )
13901393 {
13911394 count = insertCmd . ExecuteNonQuery ( vals ) ;
13921395 }
1393- catch ( SQLiteException ex )
1394- {
1395- if ( sqlite . ExtendedErrCode ( Handle ) == ExtendedResult . ConstraintNotNull )
1396- throw new NotNullConstraintViolationException ( ex . Result , ex . Message , map , obj ) ;
1397- throw ;
1398- }
1396+ }
1397+ catch ( SQLiteException ex )
1398+ {
1399+ if ( sqlite . ExtendedErrCode ( Handle ) == ExtendedResult . ConstraintNotNull )
1400+ throw new NotNullConstraintViolationException ( ex . Result , ex . Message , map , obj ) ;
1401+ throw ;
13991402 }
14001403
14011404 if ( map . HasAutoIncPK )
@@ -1410,8 +1413,6 @@ public int Insert(object? obj, string extra, Type? objType)
14101413 return count ;
14111414 }
14121415
1413- readonly ConcurrentDictionary < ( string MappedTypeFullName , string Extra ) , PreparedSqlLiteInsertCommand > _insertCommandMap = new ( ) ;
1414-
14151416 private PreparedSqlLiteInsertCommand GetInsertCommand ( TableMapping map , string extra )
14161417 {
14171418 var key = ( map . MappedType . FullName , extra ) ;
@@ -1567,10 +1568,11 @@ public int UpdateAll(IEnumerable objects, bool runInTransaction = true)
15671568 var c = 0 ;
15681569 if ( runInTransaction )
15691570 {
1570- RunInTransaction ( ( ) =>
1571+ using var db = Clone ( ) ;
1572+ db . RunInTransaction ( ( ) =>
15711573 {
15721574 foreach ( var r in objects )
1573- c += Update ( r ) ;
1575+ c += db . Update ( r ) ;
15741576 } ) ;
15751577 }
15761578 else
0 commit comments